/*
 * Copyright (c) 2019.
 * http://www.ulabcare.com
 */

package com.mt.roll.passport.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.mt.roll.core.base.BaseService;
import com.mt.roll.core.dal.entity.LoginLog;
import com.mt.roll.core.dal.entity.LoginSession;
import com.mt.roll.core.dal.entity.SysUser;
import com.mt.roll.core.dal.service.LoginLogServiceBiz;
import com.mt.roll.core.dal.service.LoginSessionServiceBiz;
import com.mt.roll.core.dal.service.SysUserServiceBiz;
import com.mt.roll.core.dto.LoginLogDto;
import com.mt.roll.core.dto.LoginUser;
import com.mt.roll.core.params.MobileParam;
import com.mt.roll.core.vo.PageVo;
import com.mt.roll.core.vo.R;
import com.mt.roll.passport.params.BatchInvalidateSessionParam;
import com.mt.roll.passport.params.QueryLoginLogParam;
import com.mt.roll.passport.params.QuerySessionParam;
import com.mt.roll.passport.params.SessionParam;
import com.mt.roll.passport.service.PassportService;
import com.mt.roll.passport.service.SessionService;
import com.mt.roll.passport.vo.LoginLogVo;
import com.mt.roll.passport.vo.LoginSessionVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.Session;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author jiff
 * @date 2019-07-20
 * @since 1.0
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class SessionServiceImpl extends BaseService implements SessionService {
    @Autowired
    private LoginSessionServiceBiz loginSessionServiceBiz;
    @Autowired
    private LoginLogServiceBiz loginLogServiceBiz;

    @Autowired
    private SysUserServiceBiz sysUserServiceBiz;

    @Autowired
    private PassportService passportService;
    @Autowired
    private FindByIndexNameSessionRepository<? extends Session> sessionRepository;


    /**
     * 获取登录会话列表
     *
     * @param param
     * @return
     */
    @Override
    public R<PageVo<LoginSessionVo>> list(QuerySessionParam param) {
        LoginUser loginUser = getLoginUser();
        QueryWrapper<LoginSession> wrapper = new QueryWrapper<>();
        wrapper.eq(ObjectUtils.isNotNull(param.getSessionId()), LoginSession.SESSION_ID, param.getSessionId())
                .eq(ObjectUtils.isNotNull(param.getMobile()), LoginSession.MOBILE, param.getMobile())
                .eq(ObjectUtils.isNotNull(param.getUserType()), LoginSession.USER_TYPE, param.getUserType())
                .eq(ObjectUtils.isNotNull(param.getAppType()), LoginSession.APP_TYPE, param.getAppType())
                .eq(ObjectUtils.isNotNull(param.getLoginType()), LoginSession.LOGIN_TYPE, param.getLoginType())
                .eq(ObjectUtils.isNotNull(param.getIp()), LoginSession.IP, param.getIp())
                .eq(StringUtils.isNotBlank(param.getDeviceClass()), LoginSession.DEVICE_CLASS, param.getDeviceClass())
                .like(StringUtils.isNotBlank(param.getDeviceBrand()), LoginSession.DEVICE_BRAND, param.getDeviceBrand())
                .like(StringUtils.isNotBlank(param.getOs()), LoginSession.OS, param.getOs())
                .like(StringUtils.isNotBlank(param.getBrowser()), LoginSession.BROWSER, param.getBrowser())
                .like(StringUtils.isNotBlank(param.getUserName()), LoginSession.USER_NAME, param.getUserName());
        //LocalDate.parse不允许null，不能使用QueryWrapper#ge(boolean condition, R column, Object val)
        if (StringUtils.isNotBlank(param.getStartTime())) {
            wrapper.ge(LoginSession.LOGIN_TIME, LocalDate.parse(param.getStartTime(), DateTimeFormatter.ISO_LOCAL_DATE).atStartOfDay());
        }
        if (StringUtils.isNotBlank(param.getEndTime())) {
            wrapper.le(LoginSession.LOGIN_TIME, LocalDate.parse(param.getEndTime(), DateTimeFormatter.ISO_LOCAL_DATE).atTime(LocalTime.MAX));
        }
        //根据登录时间倒序
        wrapper.orderByDesc(LoginSession.LOGIN_TIME);
        IPage<LoginSession> page = loginSessionServiceBiz.page(new Page<>(param.getCurrent(), param.getSize()), wrapper);
        return successful(new PageVo<>(page, LoginSessionVo.class));
    }

    /**
     * 获取登录会话日志列表
     *
     * @param param
     * @return
     */
    @Override
    public R<PageVo<LoginLogVo>> listLog(QueryLoginLogParam param) {

        LoginUser loginUser = getLoginUser();
        QueryWrapper<LoginLog> wrapper = new QueryWrapper<>();
        wrapper.eq(ObjectUtils.isNotNull(param.getSessionId()), LoginLog.SESSION_ID, param.getSessionId())
                .eq(ObjectUtils.isNotNull(param.getMobile()), LoginLog.MOBILE, param.getMobile())
                .eq(ObjectUtils.isNotNull(param.getUserType()), LoginLog.USER_TYPE, param.getUserType())
                .eq(ObjectUtils.isNotNull(param.getAppType()), LoginLog.APP_TYPE, param.getAppType())
                .eq(ObjectUtils.isNotNull(param.getLoginType()), LoginLog.LOGIN_TYPE, param.getLoginType())
                .eq(ObjectUtils.isNotNull(param.getLogoutType()), LoginLog.LOGOUT_TYPE, param.getLogoutType())
                .eq(ObjectUtils.isNotNull(param.getIp()), LoginLog.IP, param.getIp())
                .eq(StringUtils.isNotBlank(param.getDeviceClass()), LoginLog.DEVICE_CLASS, param.getDeviceClass())
                .like(StringUtils.isNotBlank(param.getDeviceBrand()), LoginLog.DEVICE_BRAND, param.getDeviceBrand())
                .like(StringUtils.isNotBlank(param.getOs()), LoginLog.OS, param.getOs())
                .like(StringUtils.isNotBlank(param.getBrowser()), LoginLog.BROWSER, param.getBrowser())
                .like(StringUtils.isNotBlank(param.getUserName()), LoginLog.USER_NAME, param.getUserName());
        //LocalDate.parse不允许null，不能使用QueryWrapper#ge(boolean condition, R column, Object val)
        //登录时间
        if (StringUtils.isNotBlank(param.getStartTime())) {
            wrapper.ge(LoginLog.LOGIN_TIME, LocalDate.parse(param.getStartTime(), DateTimeFormatter.ISO_LOCAL_DATE).atStartOfDay());
        }
        if (StringUtils.isNotBlank(param.getEndTime())) {
            wrapper.le(LoginLog.LOGIN_TIME, LocalDate.parse(param.getEndTime(), DateTimeFormatter.ISO_LOCAL_DATE).atTime(LocalTime.MAX));
        }
        //退出时间
        if (StringUtils.isNotBlank(param.getLogoutStartTime())) {
            wrapper.ge(LoginLog.LOGOUT_TIME, LocalDate.parse(param.getLogoutStartTime(), DateTimeFormatter.ISO_LOCAL_DATE).atStartOfDay());
        }
        if (StringUtils.isNotBlank(param.getLogoutEndTime())) {
            wrapper.le(LoginLog.LOGOUT_TIME, LocalDate.parse(param.getLogoutEndTime(), DateTimeFormatter.ISO_LOCAL_DATE).atTime(LocalTime.MAX));
        }
        //根据退出时间倒序
        wrapper.orderByDesc(LoginLog.LOGOUT_TIME);
        IPage<LoginLog> page = loginLogServiceBiz.page(new Page<>(param.getCurrent(), param.getSize()), wrapper);
        return successful(new PageVo<>(page, LoginLogVo.class));
    }

    /**
     * 注销用户会话
     *
     * @param param
     * @return
     */
    @Override
    public R<String> invalidateSession(SessionParam param) {
        LoginUser loginUser = getLoginUser();
        if (loginUser == null) {
            return R.unauthorized();
        }
        //记录退出时间
        passportService.saveLoginLog(LoginLogDto.builder()
                .sessionId(param.getSessionId())
                .action(LoginLogDto.ACTION_LOGOUT)
                .logoutType(LoginLogDto.LOGOUT_TYPE_MANAGE)
                .logoutTime(new Date())
                .userId(loginUser.getUserId())
                .userName(loginUser.getUserName())
                .build());
        sessionRepository.deleteById(param.getSessionId());
        return successful(param.getSessionId());
    }

    /**
     * 批量注销用户会话
     *
     * @param param
     * @return
     */
    @Override
    public R<List<String>> batchInvalidateSession(BatchInvalidateSessionParam param) {
        LoginUser loginUser = getLoginUser();
        if (loginUser == null) {
            return R.unauthorized();
        }
        for (String sessionId : param.getSessionIds()) {
            //记录退出时间
            passportService.saveLoginLog(LoginLogDto.builder()
                    .sessionId(sessionId)
                    .action(LoginLogDto.ACTION_LOGOUT)
                    .logoutType(LoginLogDto.LOGOUT_TYPE_MANAGE)
                    .logoutTime(new Date())
                    .userId(loginUser.getUserId())
                    .userName(loginUser.getUserName())
                    .build());
            sessionRepository.deleteById(sessionId);
        }
        return successful(param.getSessionIds());
    }

    /**
     * 根据手机号码注销用户会话，该手机号码所有会话都注销
     *
     * @param param
     * @return
     */
    @Override
    public R<List<String>> invalidateSession(MobileParam param) {
        LoginUser loginUser = getLoginUser();
        if (loginUser == null) {
            return R.unauthorized();
        }
        List<String> sessionIds = new ArrayList<>();
        Map<String, ? extends Session> sessions = sessionRepository.findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, param.getMobile());
        for (Session session : sessions.values()) {
            //记录退出时间
            passportService.saveLoginLog(LoginLogDto.builder()
                    .sessionId(session.getId())
                    .action(LoginLogDto.ACTION_LOGOUT)
                    .logoutType(LoginLogDto.LOGOUT_TYPE_MANAGE)
                    .logoutTime(new Date())
                    .userId(loginUser.getUserId())
                    .userName(loginUser.getUserName())
                    .build());
            sessionRepository.deleteById(session.getId());
            sessionIds.add(session.getId());
        }
        return successful(sessionIds);
    }

    /**
     * 注销所有平台用户会话
     *
     * @return
     */
    @Override
    public R<List<String>> invalidateAllPlatformSession() {
        List<String> sessionIds = new ArrayList<>();
        List<SysUser> users = sysUserServiceBiz.list();
        for (SysUser user : users) {
            R<List<String>> r = invalidateSession(MobileParam.builder().mobile(user.getMobile()).build());
            if (r.ok()) {
                sessionIds.addAll(r.getData());
            }
        }
        return successful(sessionIds);
    }


    /**
     * 获取登录用户
     *
     * @param param
     * @return
     */
    @Override
    public R<List<LoginUser>> getLoginSession(MobileParam param) {
        List<LoginUser> loginUsers = new ArrayList<>();
        Map<String, ? extends Session> sessions = sessionRepository.findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, param.getMobile());
        for (Session session : sessions.values()) {
            String loginUserJson = session.getAttribute(LoginUser.HTTP_HEADER_NAME);
            if (StringUtils.isNotBlank(loginUserJson)) {
                LoginUser loginUser = JSON.parseObject(loginUserJson, LoginUser.class);
                loginUser.setSessionId(session.getId());
                loginUsers.add(loginUser);
            }
        }
        return successful(loginUsers);
    }

}
